package ru.agiledev.aos.queryanalyzer;

import ru.agiledev.aos.commons.dto.QType;
import ru.agiledev.aos.commons.dto.SemanticQuery;
import ru.agiledev.aos.commons.dto.LightWord;
import ru.agiledev.aos.core.util.QueryUtils;

import java.util.*;

/**
 * Objects of Ineterest finder class. Contains methods for parsing query depending on its type.
 * todo: one iteration parser - we really can do it
 *
 * Created by a.kirillov
 * Date: 11/6/11 Time: 10:34 PM
 */
public class QueryParser {
    public void parse(SemanticQuery semanticQuery){
        List<LightWord> result;
        QType type = semanticQuery.getQuestionType();
        List<LightWord> query = semanticQuery.getTransformedQuery();

        switch (type){
            case STRUCTURE_DESCRIPTION:{
                //Исключение: "Из чего состоит Х?"
                if(query.get(0).getStems().contains("из")&&query.get(1).getStems().contains("что")){
                    for(int i=2; i<query.size();i++){
                        if(query.get(i).getStems().contains("состоять")){
                            result = getRestAsObjectOfInterest(semanticQuery.getQuestionType(), query.subList(i+1, query.size()), Dictionaries.getInstance().getStructureDescriptionEmptyChainProducers());
                            semanticQuery.setDistinctiveObject(null);
                            semanticQuery.setSecondObject(result);
                            return;
                        }
                    }
                }
                //Исключение: "Из каких частей состоит Х?"
                if(query.get(0).getStems().contains("из")&&query.get(1).getStems().contains("какой")){
                    for(int i=2; i<query.size();i++){
                        if(query.get(i).getStems().contains("состоять")){
                            result = getRestAsObjectOfInterest(semanticQuery.getQuestionType(), query.subList(i+1, query.size()), Dictionaries.getInstance().getStructureDescriptionEmptyChainProducers());
                            semanticQuery.setDistinctiveObject(null);
                            semanticQuery.setSecondObject(result);
                            return;
                        }
                    }
                }

                //if extra cases are analyzed and result is null, the common structure is being analyzed
                processStandardQuestionLayout(semanticQuery, Dictionaries.getInstance().getStructureDescriptionFlags(), Dictionaries.getInstance().getStructureDescriptionEmptyChainProducers());
                return;
            }
            case CHARACTERISTICS_DESCRIPTION: {
                //if extra cases are analyzed and result is null, the common structure is being analyzed
                processStandardQuestionLayout(semanticQuery, Dictionaries.getInstance().getCharacteristicsDescriptionFlags(), Dictionaries.getInstance().getCharacteristicsDescriptionEmptyChainProducers());
                return;
            }
            case WORK_DESCRIPTION:{
                //if extra cases are analyzed and result is null, the common structure is being analyzed
                processStandardQuestionLayout(semanticQuery, Dictionaries.getInstance().getWorkDescriptionFlags(), Dictionaries.getInstance().getWorkDescriptionEmptyChainProducers());
                return;
            }
            case FUNCTIONS_DESCRIPTION:{
                if(query.get(0).getStems().contains("за")&&query.get(1).getStems().contains("что")){
                    for(int i=2; i<query.size();i++){
                        if(query.get(i).getStems().contains("отвечать")){
                            result = getRestAsObjectOfInterest(semanticQuery.getQuestionType(), query.subList(i+1, query.size()), Dictionaries.getInstance().getFunctionsDescriptionFlags());
                            semanticQuery.setDistinctiveObject(null);
                            semanticQuery.setSecondObject(result);
                            return;
                        }
                    }
                }

                processStandardQuestionLayout(semanticQuery, Dictionaries.getInstance().getFunctionsDescriptionFlags(), Dictionaries.getInstance().getFunctionsDescriptionEmptyChainProducers());
                return;
            }
            case MISSION_DESCRIPTION:{
                if(query.get(0).getStems().contains("зачем")){
                    for(int i=1; i<query.size();i++){
                        if(query.get(i).getStems().contains("нужный")){
                            result = getRestAsObjectOfInterest(semanticQuery.getQuestionType(), query.subList(i+1, query.size()), Dictionaries.getInstance().getMissionDescriptionFlags());
                            semanticQuery.setDistinctiveObject(null);
                            semanticQuery.setSecondObject(result);
                            return;
                        }
                    }
                }
                processStandardQuestionLayout(semanticQuery, Dictionaries.getInstance().getMissionDescriptionFlags(), Dictionaries.getInstance().getMissionDescriptionEmptyChainProducers());
                return;
            }
            case USAGE_DESCRIPTION:{
                processStandardQuestionLayout(semanticQuery, Dictionaries.getInstance().getUsageDescriptionFlags(), Dictionaries.getInstance().getUsageDescriptionEmptyChainProducers());
                return;
            }
            case CORRESPONDS_TO_DESCRIPTION:{
                if((query.get(0).getStems().contains("к")&&query.get(1).getStems().contains("что"))||(query.get(0).getStems().contains("к")&&query.get(1).getStems().contains("кто"))){
                    for(int i=2; i<query.size();i++){
                        if(query.get(i).getStems().contains("относиться")){
                            result = getRestAsObjectOfInterest(semanticQuery.getQuestionType(), query.subList(i+1, query.size()), Dictionaries.getInstance().getCorrespondsToDescriptionFlags());
                            semanticQuery.setDistinctiveObject(null);
                            semanticQuery.setSecondObject(result);
                            return;
                        }
                    }
                }

                //all other cases for this query type are covered with this method
                correspondsToAnalysis(semanticQuery);
                return;
            }
            case DIFFERENCES_DESCRIPTION:{

                twoObjectsQueriesAnalysis(semanticQuery, Dictionaries.getInstance().getDifferencesDescriptionFlags(),
                        Dictionaries.getInstance().getDifferencesDescriptionEmptyChainProducers(),
                        Dictionaries.getInstance().getDifferencesDescriptionConjunctionPointers());

                return;
            }
            case COMMONS_DESCRIPTION:{
                twoObjectsQueriesAnalysis(semanticQuery, Dictionaries.getInstance().getCommonsDescriptionFlags(),
                        Dictionaries.getInstance().getCommonsDescriptionEmptyChainProducers(),
                        Dictionaries.getInstance().getCommonsDescriptionConjunctionPointers());

                return;
            }
            case FEATURES_DESCRIPTION:{
                featuresDescriptionAnalysis(semanticQuery);
                return;
            }
            case POSSIBILITIES_DESCRIPTION:{
                break;
            }
            case GOALS_DESCRIPTION: {
                break;
            }
            case SETS_CHANGES_DESCRIPTION:{
                break;
            }
        }
    }

    /**
     * Method return the tail of query containing objects of interest filtered from empty chain producers
     * @param subQuery subset of initial list of words
     * @param emptyChains empty chains dictionary for current subquery depending on query type
     * @return list of LightWords representing the object of interest situated at the end of the query
     */
    private List<LightWord> getRestAsObjectOfInterest(QType type, List<LightWord> subQuery, Set<String> emptyChains){
        List<LightWord> result = new ArrayList<LightWord>();

        boolean extraTrailing = true;
        for(int i=0; i< subQuery.size();i++){

            LightWord lightWord = subQuery.get(i);
            if(!QueryUtils.isWordStemsInDictionary(lightWord, emptyChains)){

                if(type.equals(QType.FUNCTIONS_DESCRIPTION)){
                    if(extraTrailing&&lightWord.getStems().contains("возлагать")){
                        if(subQuery.size()>(1+i)){
                            if(subQuery.get(i+1).getStems().contains("на")){
                                i = i+1;
                                result.removeAll(result);
                                extraTrailing = false;
                                continue;
                            }
                        }
                    }
                }

                result.add(lightWord);
            } else {
                //обработка ситуации вида "какими характеристиками главным образом обладает X?"
                result.removeAll(result);
            }
        }

        return result;
    }


    /**
     *  Method provides common functionality for defining objects of interest in queries which structure is common
     * @param semanticQuery input query as SemanticQuery
     * @param allowedDictionary allowed flags
     * @param emptyChainProducers empty chain producers
     */
    private void processStandardQuestionLayout(SemanticQuery semanticQuery, HashSet<String> allowedDictionary, HashSet<String> emptyChainProducers){
        List<LightWord> result;
        List<LightWord> query = semanticQuery.getTransformedQuery();

        for(int i=0; i<query.size();i++){
            if(QueryUtils.isWordStemsInDictionary(query.get(i), allowedDictionary)){

                result = getRestAsObjectOfInterest(semanticQuery.getQuestionType(), query.subList(i+1, query.size()), emptyChainProducers);
                semanticQuery.setDistinctiveObject(query.get(i));
                semanticQuery.setSecondObject(result);

                if(semanticQuery.getQuestionType().equals(QType.USAGE_DESCRIPTION)){
                    List<LightWord> additive = leftAdditionAnalysys(query.subList(0, i), Dictionaries.getInstance().getUsageDescriptionAdditiveEmptyChainProducers());
                    semanticQuery.setAdditiveObject(additive);
                }
            }
        }
    }

    /**
     * Extra-method for left-part analysis of queries that need some additional information like "В какой отрасли используется красный лазер?"
     * @param query left part of query
     * @param addicitveEmptyChainProducers addition empty chain producers
     * @return list of additions as LightWord objects
     */
    private List<LightWord> leftAdditionAnalysys(List<LightWord> query, HashSet<String> addicitveEmptyChainProducers){
        List<LightWord> result = new ArrayList<LightWord>();
        for(int i=0; i<query.size();i++){
            if(query.get(i).getStems().contains("в")){
                if((i+1)<query.size()){
                    if(QueryUtils.isWordStemsInDictionary(query.get(i+1), addicitveEmptyChainProducers)){
                        i++;
                        continue;
                    }
                }
            } else {
                if(!QueryUtils.isWordStemsInDictionary(query.get(i), addicitveEmptyChainProducers)){
                    result.add(query.get(i));
                }
            }
        }
        return result;

        /*
          как в металлургии в основном применяется Х
          как главным образом в европе используется газ

          каков главный принцип использования Х
        */
        /*
        В какой отрасли применяется Х?
        Как в отрасли применяется Х?
        Как в отрасли главным образом применяется х?

        в -> какой, основном, принципе,
        главным - образом

        */
    }

    /**
     * Method for corresponds to query analysis and additive object capturing
     * @param semanticQuery initial SemanticQuery object
     */
    private void correspondsToAnalysis(SemanticQuery semanticQuery){
        List<LightWord> query = semanticQuery.getTransformedQuery();

        List<LightWord> object = new ArrayList<LightWord>();
        List<LightWord> addition = new ArrayList<LightWord>();
        boolean objectFlag = false;

        for(int i=0; i<query.size();i++){
            if(QueryUtils.isWordStemsInDictionary(query.get(i), Dictionaries.getInstance().getCorrespondsToDescriptionFlags())){
                semanticQuery.setDistinctiveObject(query.get(i));

                for(int j=i+1; j<query.size();j++){

                    if(QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getCorrespondsToDescriptionEmptyChainPointers())){
                        objectFlag = true;
                        continue;
                    }

                    if(!objectFlag){
                        if(!QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getCorrespondsToDescriptionEmptyChainProducers())){
                            addition.add(query.get(j));
                        }
                    } else {
                        if(!QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getCorrespondsToDescriptionEmptyChainProducers())){
                            object.add(query.get(j));
                        }
                    }
                }
            }
        }
        semanticQuery.setAdditiveObject(addition);
        semanticQuery.setSecondObject(object);
    }

    /**
     * Method deals with queries that contain two different objects of interest and in further analysis their differences or commons must be defined
     * @param semanticQuery SemanticQuery object
     * @param allowedDictionary HashSet of flags for current query type
     * @param emptyChainProducers HashSet of empty chain producers for current query type
     * @param conjunctionPointers HashSet of conjunction pointers or delimiters between objects of interest for current query type
     */
    private void twoObjectsQueriesAnalysis(SemanticQuery semanticQuery, HashSet<String> allowedDictionary, HashSet<String> emptyChainProducers, HashSet<String> conjunctionPointers){
        List<LightWord> query = semanticQuery.getTransformedQuery();
        List<LightWord> firstObject = new ArrayList<LightWord>();
        List<LightWord> secondObject = new ArrayList<LightWord>();

        boolean isSecondObject = false;

        for(int i=0; i<query.size();i++){

            if(QueryUtils.isWordStemsInDictionary(query.get(i), allowedDictionary)){
                semanticQuery.setDistinctiveObject(query.get(i));

                for(int j=i+1; j<query.size();j++){
                    if(query.get(j-1).getStems().contains("общий")&&query.get(j).getStems().contains("черта")) continue;

                    if(QueryUtils.isWordStemsInDictionary(query.get(j), conjunctionPointers)){
                        isSecondObject = true;
                        continue;
                    }

                    if(!isSecondObject){
                        if(!QueryUtils.isWordStemsInDictionary(query.get(j), emptyChainProducers)){
                            firstObject.add(query.get(j));
                        } else {
                            firstObject.removeAll(firstObject);
                        }
                    } else {
                        if(!QueryUtils.isWordStemsInDictionary(query.get(j), emptyChainProducers)){
                            secondObject.add(query.get(j));
                        } else {
                            secondObject.removeAll(secondObject);
                        }
                    }
                }
            }
        }

        //we should perform search of first object on the left side of flag in case if it is not defined properly
        if(isSecondObject&&(firstObject.size()==0)){
            for(int i=0; i<query.size();i++){
                if(!QueryUtils.isWordStemsInDictionary(query.get(i), allowedDictionary)){
                    if(!QueryUtils.isWordStemsInDictionary(query.get(i), emptyChainProducers)){
                        firstObject.add(query.get(i));
                    }
                } else break;
            }
        } else if(!isSecondObject){          //we need this approach if there is no conjunction. Example: "Чем X несоответствует Y?"
            secondObject.addAll(firstObject);
            firstObject.removeAll(firstObject);

            for(int i=0; i<query.size();i++){
                if(!QueryUtils.isWordStemsInDictionary(query.get(i), allowedDictionary)){
                    if(!QueryUtils.isWordStemsInDictionary(query.get(i), emptyChainProducers)){
                        firstObject.add(query.get(i));
                    }
                } else break;
            }
        }


        semanticQuery.setSecondObject(firstObject);
        semanticQuery.setThirdObject(secondObject);
    }


    private void featuresDescriptionAnalysis(SemanticQuery semanticQuery) {
        List<LightWord> query = semanticQuery.getTransformedQuery();

        List<LightWord> object = new ArrayList<LightWord>();
        List<LightWord> secondObject = new ArrayList<LightWord>();
        List<LightWord> addition = new ArrayList<LightWord>();

        boolean objectFlag = false;
        /*
        //Features descriptions questions section
    String[] features = {
            "Какие особенности есть у Х?",
            "Каковы основные отличительные черты Х?",

            "специфика Х",
            "Каковы главные отличительные черты Х?",
            "В чем уникальность Х?",
            "главные отличительные черты Х",

            "чем Х уникален?",
            "В чем Х особенный?",
            "В чем Х специфичный?",
            "Каковы особенности работы Х в ситуации У?",
            "отличительные черты функционирования Х при У?",
            "ключевые особенности действия Х в обстоятельствах У",
            "Как ведет себя Х в условиях У?",
            "Как функционирует Х в обстоятельствах У?",
            "Как действует Х в условиях У?"};

    String[] featuresExtended = {
            "Каковы особенности Х?",
            "Каковы отличительные черты Х?",

            "Какие особенности имеет Х?",
            "Какими особенностями обладает Х?",
            "В чем уникальность Х?",
            "отличительные черты Х?",
            "В чем особенности Х?",
            "В чем Х уникален?",
            "чем Х особенный?",
            "Каковы особенности работы Х в ситуации У?",
            "Каковы отличительные черты функционирования Х при У?",
            "Каковы особенности действия Х в обстоятельствах У?",
            "Как ведет себя Х в условиях У?",
            "Как работает Х в обстоятельствах У?",
            "Как действует Х в ситуации У?"};
        */
        for(int i=0; i<query.size();i++){
            //if object found
            if(QueryUtils.isWordStemsInDictionary(query.get(i), Dictionaries.getInstance().getFeaturesDescriptionFlags())){

                semanticQuery.setDistinctiveObject(query.get(i));
                boolean flag = false;
                for(int j=i+1; j<query.size();j++){
                    if(query.get(j-1).getStems().contains("отличительный")&&query.get(j).getStems().contains("черта")) continue;
                    if(!QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getFeaturesDescriptionEmptyChainProducers())){
                        if(QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getFeaturesDescriptionAdditiveEmptyChainProducers())){
                            addition.add(query.get(j));
                            continue;
                        }

                        if(QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getFeaturesDescriptionConjunctionPointers())){
                            flag = true;
                        }

                        if(flag){
                            secondObject.add(query.get(j));
                        } else {
                            object.add(query.get(j));
                        }

                    }
                }

                /*
                "чем Х уникален?"
                "В чем Х особенный?"
                */
                if(object.size()==0){
                    for(int j=0; j<query.size();j++){
                        if(!QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getFeaturesDescriptionFlags())){
                            if(!QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getFeaturesDescriptionEmptyChainProducers())
                                    &&!QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getFeaturesDescriptionAdditiveEmptyChainProducers())){
                                object.add(query.get(j));
                            }
                        } else break;
                    }
                }
                semanticQuery.setAdditiveObject(addition);
                semanticQuery.setSecondObject(object);
                semanticQuery.setThirdObject(secondObject);
                return;

            } else if(QueryUtils.isWordStemsInDictionary(query.get(i), Dictionaries.getInstance().getFeaturesDescriptionAdditiveEmptyChainProducers())){ // and here if object is not found
                /*Как работает Х в обстоятельствах У?*/
                boolean flag = false;
                addition.add(query.get(i));
                for(int j=i+1; j<query.size();j++){
                    if(!QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getFeaturesDescriptionEmptyChainProducers())){

                        if(QueryUtils.isWordStemsInDictionary(query.get(j), Dictionaries.getInstance().getFeaturesDescriptionConjunctionPointers())){
                            flag = true;
                        }

                        if(flag){
                            secondObject.add(query.get(j));
                        } else {
                            object.add(query.get(j));
                        }


                    }
                }
                semanticQuery.setAdditiveObject(addition);
                semanticQuery.setSecondObject(object);
                semanticQuery.setThirdObject(secondObject);
                return;
            }

        }

    }


}
